前一天我們提到了,Low-level API 裡面的 State Machine, Rive Text, Rive Events 的語法,也跟 High-level API 不太一樣,這些語法散落在整個官方文件裡面,在這裡一起整理給大家。
其實底層邏輯完全一樣,只是原本是從 Rive 實體拿,現在改成從 init 時存起來的 stateMachine 拿。然後多了三組 asXXX,分別對應到三種 input。
const stateMachineName = 'State Machine 1'
const inputIndex = 0
// High-level API.
r.stateMachineInputs[stateMachineName][inputIndex].fire()
r.stateMachineInputs[stateMachineName][inputIndex].value
r.stateMachineInputs[stateMachineName][inputIndex].value
// Low-level API
stateMachine.input(inputIndex).asTrigger().fire()
stateMachine.input(inputIndex).asNumber().value
stateMachine.input(inputIndex).asBool().value
有點坑的地方在於,如果有用 TypeScript 的話,asTrigger & asNumber & asBool 回傳的都是 number | boolean | undefined,而且沒有寫 generic,所以這可能要自己處理一下。
也跟剛剛的情況完全一樣,底層邏輯完全相同,原本也是從 Rive 實體拿,現在改成從 init 時存起來的 artboard 拿
// High-level API.
r.getTextRunValue('myKey')
r.setTextRunValue('myKey', 'newValue')
// Low-level API
artboard.textRun('myKey').text
artboard.textRun('myKey').text = 'newValue'
這部分就比較複雜一點,原本直接註冊後拿值就好,現在沒有註冊這個概念,而是要自己在 render loop 裡面處理 event 的邏輯。event 因為是更新 render 要用的資料,所以屬於 update 那一段,大概會長這樣。
// High-leve; API.
import { EventType } from '@rive-app/canvas'
r.on(EventType.RiveEvent, (riveEvent) => {
const eventName = riveEvent.data.name
const properties = riveEvent.data.properties
})
// Low-level API.
renderer.clear()
for (let i = 0; i < stateMachine.reportedEventCount(); i += 1) {
// 發送給 Rive
const e = stateMachine.reportedEventAt(i)
// 有需要的話,再發送給前端自己,通常會寫成一個 customEvent
// 所以註冊 event 的話,要從 canvas 元素那邊下手
// canvas.dispatchEvent(new CustomEvent('riveEvent', { detail: e }))
}
stateMachine.advance(time)
artboard.advance(time)
你看的沒錯,你要先用 reportedEventCount 拿到 event 數量後,再用迴圈自己跑一次發送,語法稍微囉嗦一點。不過這樣寫的好處是比較靈活,可以做一些奇怪的操作,例如攔截、debounce、throttle 等等。
其實這算是 Low-level API 的好處,因為自己刻 render loop,所以可以在 update & render 之前做一些操作,更精細的控制整個渲染流程。明天會這對這一點,分享一些我們實作上有用到的 cookbook。